﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Pay.Core {
    /**
     * 邀请码生成器，算法原理：<br/>
     * 1) 获取id: 1127738 <br/>
     * 2) 使用自定义进制转为：gpm6 <br/>
     * 3) 转为字符串，并在后面加'o'字符：gpm6o <br/>
     * 4）在后面随机产生若干个随机数字字符：gpm6o7 <br/>
     * 转为自定义进制后就不会出现o这个字符，然后在后面加个'o'，这样就能确定唯一性。最后在后面产生一些随机字符进行补全。<br/>
     */
     /** remark :同一id 生成邀请码不唯一  自定义一个字符串e*/
    public static class InvitationCodeHelp2 {
        /** 自定义进制(0,1没有加入,容易与o,l混淆) */
        private static char[] r = new char[] { 'Q', 'w', 'E', '8', 'a', 'S', '2', 'd', 'Z', 'x', '9', 'c', '7', 'p', 'O', '5', 'i', 'K', '3', 'm', 'j', 'U', 'f', 'r', '4', 'V', 'y', 'L', 't', 'N', '6', 'b', 'g', 'H' };
        /** (不能与自定义进制有重复) */
        private static char b = 'o';
        private static string e = "AygSxhj";
        /**进制长度*/
        private static int l = r.Length;
        /**序列最小长度*/
        private static int s = 6;
        public static String toSerialCode(long id) {
            char[] buf = new char[32];
            int charPos = 32;

            while ((id / l) > 0) {
                buf[--charPos] = r[(int)(id % l)];
                id /= l;
            }
            buf[--charPos] = r[(int)(id % l)];
            String str = new String(buf, charPos, (32 - charPos));
            //不够长度的自动随机补全
            if (str.Length < s) {
                StringBuilder sb = new StringBuilder();
                //sb.Append(b);
                //Random rnd = new Random();
                //for (int i = 1; i < s - str.Length; i++) {
                //    sb.Append(r[rnd.Next(l)]);
                //}
               sb.Append(e.Substring(0, s - str.Length));  
                str += sb.ToString();
            }
            return str;
        }
        public static long codeToId(string code) {
            char[] chs = code.ToCharArray();
            long res = 0L;
            for (int i = 0; i < chs.Length; i++) {
                int ind = 0;
                for (int j = 0; j < l; j++) {
                    if (chs[i] == r[j]) {
                        ind = j;
                        break;
                    }
                }
                if (chs[i] == b) {
                    break;
                }
                if (i > 0) {
                    res = res * l + ind;
                } else {
                    res = ind;
                }

            }
            return res;
        }



    }
    public class InvitationCodeHelp {
        //验证码长度
        private static int LEN = 6;
        //验证码字符列表
        private static readonly char[] STUFFS = { 'A', '6', 'B', 'C', '2', 'D', 'E', 'F', '4', 'G', 'H', '5', 'I', 'J', '1', 'K', 'L', '7', 'M', 'N', 'P', 'Q', 'R', '8', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', '3' };

        private static int PERMUTATION = permutation(LEN);
        private static int MAX_COMBINATION = combination(STUFFS.Length, LEN);
       
        private static int combination(int n, int m) {
            int com = 1;
            for (int i = n - m + 1; i <= n; ++i) {
                com *= i;
            }
            for (int i = 2; i <= m; ++i) {
                com /= i;
            }
            return com;
        }

        private static int permutation(int n) {
            int per = 1;
            for (int i = 2; i <= n; ++i) {
                per *= i;
            }
            return per;
        }

        public static int decode(String code) {
            if (code.Length != LEN) {
                throw new Exception("invalid code");
            }
            char[] chars = new char[LEN];
            for (int i = 0; i < LEN; ++i) {
                chars[i] = char.Parse(code.Substring(i, 1));
            }
            int com = combination(chars);
            int per = permutation(chars);
            return com * PERMUTATION + per;
        }

        public static String toSerialCode(long val) {
            int com = (int)val / PERMUTATION;
            if (com >= MAX_COMBINATION) {
                throw new Exception("id can't be greater than 652458239");
            }
            int per = (int)val % PERMUTATION;
            char[] chars = combination(com);
            chars = permutation(chars, per);
            return new String(chars);
        }

        private static char[] combination(int com) {
            char[] chars = new char[LEN];
            int start = 0;
            int index = 0;
            while (index < LEN) {
                for (int s = start; s < STUFFS.Length; ++s) {
                    int c = combination(STUFFS.Length - s - 1, LEN - index - 1);
                    if (com >= c) {
                        com -= c;
                        continue;
                    }
                    chars[index++] = STUFFS[s];
                    start = s + 1;
                    break;
                }
            }
            return chars;
        }

        private static char[] Sort(char[] src) {
            char[] sort = new char[src.Length];
            int index = 0;
            for (int i = 0; i < STUFFS.Length; ++i) {
                if (find(src, STUFFS[i]) != -1) {
                    sort[index++] = STUFFS[i];
                }
            }
            return sort;
        }

        private static int combination(char[] chars) {
            int[] offset = new int[LEN];
            char[] sort = Sort(chars);
            for (int i = 0; i < sort.Length; ++i) {
                offset[i] = find(STUFFS, sort[i]);
                if (offset[i] == -1) {
                    throw new Exception("invalid code");
                }
            }
            int com = 0;
            for (int i = 0; i < offset.Length; ++i) {
                if (i == 0) {
                    if (offset[0] == 0) {
                        continue;
                    }
                    for (int n = 0; n < offset[0]; ++n) {
                        com += combination(STUFFS.Length - n - 1, LEN - 1);
                    }
                    continue;
                }

                if (offset[i] - offset[i - 1] <= 1) {
                    continue;
                }

                for (int n = offset[i - 1] + 1; n < offset[i]; ++n) {
                    com += combination(STUFFS.Length - n - 1, LEN - i - 1);
                }
            }

            return com;
        }

        private static char[] permutation(char[] chars, int per) {
            char[] tmpchars = new char[chars.Length];
            Array.Copy(chars, 0, tmpchars, 0, chars.Length);
            int[] offset = new int[chars.Length];
            int step = chars.Length;
            for (int i = chars.Length - 1; i >= 0; --i) {
                offset[i] = per % step;
                per /= step;
                step--;
            }

            for (int i = 0; i < chars.Length; ++i) {
                if (offset[i] == 0)
                    continue;
                char tmp = tmpchars[i];
                tmpchars[i] = tmpchars[i - offset[i]];
                tmpchars[i - offset[i]] = tmp;
            }

            return tmpchars;
        }

        private static int find(char[] chars, char ch) {
            for (int i = 0; i < chars.Length; ++i) {
                if (chars[i] == ch) {
                    return i;
                }
            }
            return -1;
        }

        private static int permutation(char[] chars) {
            char[] sort = Sort(chars);
            int[] offset = new int[chars.Length];
            for (int i = chars.Length - 1; i >= 0; --i) {
                int f = find(chars, sort[i]);
                offset[i] = i - f;
                char tmp = chars[i];
                chars[i] = chars[i - offset[i]];
                chars[i - offset[i]] = tmp;
            }
            int per = 0;
            int step = 1;
            for (int i = 0; i < offset.Length; ++i) {
                per = per * step + offset[i];
                step++;
            }
            return per;
        }

    }
    /// <summary>
    /// 6位唯一随机验证码(用于关注微信公众号验证)
    /// </summary>
    public static class RandomCodeHelp {
        /** 自定义进制（不能重复且最好不要0、1这些容易混淆的字符） */
        private static char[] r = new char[] { 'm', 'j', 'u', 'f', 'r', '4', 'v', 'y', 't', 'n','q', 'w', 'e', '8', 's', 'k', '3', '6', 'b', '2', 'd', 'z', 'x', '9', 'c', '7', 'p', '5', 'g', 'h', 'o', 'i' };

        /** 定义一个字符用来补全邀请码长度（该字符前面是计算出来的验证码，后面是用来补全用的，不能在自定义进制中出现） */
        private static char b = 'a';
        /** 补位字符串 */
        private static char[] e = new char[] {'t','g','s','h','j','b' };

        /** 进制长度 */
        private static int binLen = r.Length;

        /** 验证码长度 */
        private static int s = 6;
        public static string createCode(long id) {
            char[] buf = new char[32];
            int charPos = 32;

            while ((id / binLen) > 0) {
                int ind = (int)(id % binLen);
                buf[--charPos] = r[ind];
                id /= binLen;
            }
            buf[--charPos] = r[(int)(id % binLen)];
            String str = new String(buf, charPos, (32 - charPos));
            // 不够长度的自动补全
            if (str.Length < s) {
                StringBuilder sb = new StringBuilder();
                sb.Append(b);
                Random rnd = new Random();
                for (int i = 1; i < s - str.Length; i++) {
                    sb.Append(e[rnd.Next(0, e.Length - 1)]);
                }
                str += sb.ToString();
            }
            return str;
        }
        public static long decode(string code) {
            char[] chs = code.ToCharArray();
            long res = 0L;
            for (int i = 0; i < chs.Length; i++) {
                int ind = 0;
                for (int j = 0; j < binLen; j++) {
                    if (chs[i] == r[j]) {
                        ind = j;
                        break;
                    }
                }
                if (chs[i] == b) {
                    break;
                }
                if (i > 0) {
                    res = res * binLen + ind;
                } else {
                    res = ind;
                }

            }
            return res;
        }
    }
    public static class RedMakeHelp {
        /// <summary>
        /// 精度
        /// </summary>
        public const int DEGREE = 1000000;
        /// <summary>
        /// 用户每日能领取的红钻 生成发放的时间和红钻
        /// </summary>
        /// <param name="totalRed"></param>
        /// <returns></returns>
        public static Dictionary<DateTime, List<decimal>> GetTimeAndRed(DateTime dt, decimal totalRed) {
            int n = Convert.ToInt32(totalRed * DEGREE);
            int l = new Random().Next(25, 30);
            randomNum(l, n, out List<decimal> list);
            Dictionary<DateTime, List<decimal>> dic = new Dictionary<DateTime, List<decimal>>();
            int s = 0;
            Random rd = new Random(10);
            for (int i = 1; i < 7; i++) {
                DateTime endtime = dt.AddHours(i * 4);
                DateTime starttime = endtime.AddHours(-1);
                DateTime newtime = RandomTime(starttime, endtime, rd);

                if (i < 6) {
                    int pagesize = rd.Next(4, 6);
                    s += pagesize;
                    dic.Add(newtime, list.Skip((i - 1) * pagesize).Take(pagesize).ToList());
                } else
                    dic.Add(newtime, list.Skip(s).Take(l - s).ToList());
            }
            return dic;
        }
        /// <summary>
        /// 随机生成时间
        /// </summary>
        /// <param name="startime"></param>
        /// <param name="endtime"></param>
        /// <param name="rd"></param>
        /// <returns></returns>
        public static DateTime RandomTime(DateTime startime, DateTime endtime, Random rd) {
            TimeSpan tsp = endtime - startime;
            double second = rd.Next(0, (int)tsp.TotalMilliseconds);
            DateTime newtime = startime.AddMilliseconds(second);
            return newtime;
        }
        /// <summary>
        /// 随机生成红钻
        /// </summary>
        /// <param name="n">生成的数量</param>
        /// <param name="L">用户每日领的红钻</param>
        /// <param name="list"></param>
        public static void randomNum(int n, int total, out List<decimal> list) {
            Random rand = new Random(30);
            int sum = 0;
            int temp = total;
            list = new List<decimal>();
            for (int i = 1, j; i < n; i++) {
                j = rand.Next(total / 30 - 10, total / 30 + 10);
                temp -= j;
                list.Add(((decimal)j / DEGREE));
                sum += j;
            }
            list.Add((decimal)(total - sum) / DEGREE);

        }
    }
}
